package com.ityls.springsecuritystudy.config;

import com.ityls.springsecuritystudy.handler.*;
import com.ityls.springsecuritystudy.service.MyUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import java.util.Collection;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Autowired
    private MyUserDetailsService myUserDetailsService;

    @Autowired
    private PersistentTokenRepository repository;

    // Security详细配置
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        // 自定义表单登录
        http.formLogin(form -> {
            form.loginPage("/login.html") // 自定义登录页面
                    .usernameParameter("username") // 表单中的用户名项
                    .passwordParameter("password") // 表单中的密码项
                    .loginProcessingUrl("/login") //登录路径，表单向该路径提交，提交后自动执行UserDetailsService的方法
                    // .successForwardUrl("/main.html") //登录成功后跳转的路径
                    .successHandler(new MyLoginSuccessHandler()) // 登录成功处理器
                    .failureHandler(new MyLoginFailureHandler()); //登录失败后跳转的路径
                    // .failureForwardUrl("/fail.html"); //登录失败后跳转的路径
        });

        // 权限拦截配置
        http.authorizeHttpRequests(resp -> {
            resp.requestMatchers("/login.html","/fail.html").permitAll(); // 不需要认证的资源
            resp.requestMatchers("/css/*.css","/js/*.js","/img/**").permitAll(); // 静态资源不需要认证
            // resp.requestMatchers("/reportform/find").hasAnyAuthority("/reportform/find");
            // resp.requestMatchers("/salary/find").hasAnyAuthority("/salary/find");
            // resp.requestMatchers("/staff/find").hasAnyAuthority("/staff/find");
            resp.anyRequest().authenticated();//其余所有请求都需要认证
            /**
             * access的参数是一个函数式接口
             * 方法的第一个参数代表认证对象，可以获取认证用户的权限集合
             * 方法的第二个参数代表网络环境，可以获取当前请求的路径
             */
            // resp.anyRequest().access((authentication, requestContext) -> {
            //     // 获取认证的用户权限
            //     Collection<? extends GrantedAuthority> authorities = authentication.get().getAuthorities();
            //     // 获取请求的URL路径
            //     String uri = requestContext.getRequest().getRequestURI();
            //     // 将URL路径封装为权限对象
            //     SimpleGrantedAuthority authority = new SimpleGrantedAuthority(uri);
            //     // 判断用户的权限集合是否包含请求的URL权限对象
            //     boolean result = authorities.contains(authority);
            //     return new AuthorizationDecision(result);
            // });
        });

        http.logout(logout -> {
            logout.logoutUrl("/logout") // 退出登录路径
                    // .logoutSuccessUrl("/login.html") // 退出登录后跳转的路径
                    .logoutSuccessHandler(new MyLogoutSuccessHandler()) // 自定义退出成功处理器
                    .clearAuthentication(true) //清除认证状态，默认为true
                    .invalidateHttpSession(true); // 销毁HttpSession对象，默认为true
        });

        // 记住我配置
        http.rememberMe(remember -> {
            remember.userDetailsService(myUserDetailsService) //认证逻辑对象
                    .tokenRepository(repository) //持久层对象
                    .tokenValiditySeconds(30); //保存时间，单位：秒
        });

        // 会话配置
        // http.sessionManagement(session ->{
        //     // 会话过期跳转的页面
        //     session.invalidSessionUrl("/login");
        // });

        // 会话配置
        http.sessionManagement(session ->{
            // 会话失效跳转的页面
            //       session.invalidSessionUrl("/login");
            // 会话失效处理器
            session.invalidSessionStrategy(new MyInvalidSessionStrategy());
            //最大并发会话数,设置单个用户允许同时在线的最大会话数,重新登录的会话会踢掉旧的会话.
            // session.maximumSessions(1);
            //当会话达到最大值时，是否保留已经登录的用户，默认为false
            session.maximumSessions(1).maxSessionsPreventsLogin(true);
        });





        // 关闭csrf防护
        http.csrf(csrf ->{
            csrf.disable();
        });

        //异常处理
        http.exceptionHandling(exception ->{
            exception.accessDeniedHandler(new MyAccessDeniedHandler());
        });


        return http.build();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    // 注入SessionRegistry
    @Bean
    public SessionRegistry sessionRegistry(){
        return new SessionRegistryImpl();
    }

}
